package com.mongo.service.impl;

import com.mongo.func.SFunction;
import com.mongo.service.BaseService;
import com.mongo.service.LambdaGrid;
import com.mongodb.client.gridfs.model.GridFSFile;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import static com.mongo.util.ReflectionUtil.findField;

/**
 * @author xiazhun
 * @create 2022/5/27
 */
public class LambdaGridQuery extends BaseService<GridFSFile> implements LambdaGrid {
    private final Query query;

    private final List<Criteria> criterias;

    public LambdaGridQuery() {
        query = new Query();
        criterias = new ArrayList<>();
    }

    @Override
    public Query getQuery() {
        criterias.forEach(query::addCriteria);
        return query;
    }

    @Override
    public List<GridFSFile> list() {
        arrayConstructor();
        return gridListFind(query);
    }

    @Override
    public GridFSFile one() {
        return queryOne(new Query());
    }

    @Override
    public GridFSFile query() {
        arrayConstructor();
        return gridQueryOne(query);
    }

    @Override
    public LambdaGrid eq(SFunction<GridFSFile, ?> function, Object value) {
        Field field = findField(function);
        Assert.notNull(field,"无法获取引用字段");
        Criteria criteria = Criteria.where(field.getName()).is(value);
        setCriteria(criteria);
        return this;
    }

    @Override
    public LambdaGrid eq(boolean condition, SFunction<GridFSFile, ?> function, Object value) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            Criteria criteria = Criteria.where(field.getName()).is(value);
            setCriteria(criteria);
        }
        return this;
    }

    @Override
    public LambdaGrid in(SFunction<GridFSFile, ?> function, Object ...value) {
        return in(true,function,value);
    }

    @Override
    public LambdaGrid in(boolean condition, SFunction<GridFSFile, ?> function, Object... value) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            Criteria criteria = Criteria.where(field.getName()).in(value);
            setCriteria(criteria);
        }
        return this;
    }

    @Override
    public LambdaGrid in(SFunction<GridFSFile, ?> function, Collection<?> value) {
        return in(true,function,value);
    }

    @Override
    public LambdaGrid in(boolean condition, SFunction<GridFSFile, ?> function, Collection<?> value) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            Criteria criteria = Criteria.where(field.getName()).in(value);
            setCriteria(criteria);
        }
        return this;
    }


    @Override
    public LambdaGrid lt(SFunction<GridFSFile, ?> function, Object value) {
        return lt(true,function,value);
    }

    @Override
    public LambdaGrid lt(boolean condition, SFunction<GridFSFile, ?> function, Object value) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            Criteria criteria = Criteria.where(field.getName()).lt(value);
            setCriteria(criteria);
        }
        return this;
    }


    @Override
    public LambdaGrid gt(SFunction<GridFSFile, ?> function, Object value) {
        return gt(true,function,value);
    }

    @Override
    public LambdaGrid gt(boolean condition, SFunction<GridFSFile, ?> function, Object value) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            Criteria criteria = Criteria.where(field.getName()).gt(value);
            setCriteria(criteria);
        }
        return this;
    }

    @Override
    public LambdaGrid lte(SFunction<GridFSFile, ?> function, Object value) {
        return lte(true,function,value);
    }

    @Override
    public LambdaGrid lte(boolean condition, SFunction<GridFSFile, ?> function, Object value) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            Criteria criteria = Criteria.where(field.getName()).lte(value);
            setCriteria(criteria);
        }
        return this;
    }

    @Override
    public LambdaGrid gte(SFunction<GridFSFile, ?> function, Object value) {
        return gte(true,function,value);
    }

    @Override
    public LambdaGrid gte(boolean condition, SFunction<GridFSFile, ?> function, Object value) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            Criteria criteria = Criteria.where(field.getName()).gte(value);
            setCriteria(criteria);
        }
        return this;
    }

    @Override
    public LambdaGrid notEq(SFunction<GridFSFile, ?> function, Object value) {
        return notEq(true,function,value);
    }

    @Override
    public LambdaGrid notEq(boolean condition, SFunction<GridFSFile, ?> function, Object value) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            Criteria criteria = Criteria.where(field.getName()).ne(value);
            setCriteria(criteria);
        }
        return this;
    }

    @Override
    public LambdaGrid allEq(SFunction<GridFSFile, ?> function, Collection<?> value) {
        return allEq(true,function,value);
    }

    @Override
    public LambdaGrid allEq(boolean condition, SFunction<GridFSFile, ?> function, Collection<?> value) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            Criteria criteria = Criteria.where(field.getName()).all(value);
            setCriteria(criteria);
        }
        return this;
    }

    @Override
    public LambdaGrid like(boolean condition, SFunction<GridFSFile, ?> function, Object value, String regex) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            Pattern pattern;
            if (Objects.nonNull(regex)) {
                pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
            } else {
                pattern = Pattern.compile("^.*" + value + ".*$", Pattern.CASE_INSENSITIVE);
            }
            Criteria criteria = Criteria.where(field.getName()).regex(pattern);
            setCriteria(criteria);
        }
        return this;
    }

    @Override
    public LambdaGrid like(SFunction<GridFSFile, ?> function, Object value) {
        return like(true,function,value,null);
    }

    @Override
    public LambdaGrid desc(SFunction<GridFSFile, ?> function) {
        return desc(true,function);
    }

    @Override
    public LambdaGrid desc(boolean condition, SFunction<GridFSFile, ?> function) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            this.query.with(Sort.by(Sort.Order.desc(field.getName())));
        }
        return this;
    }

    @Override
    public LambdaGrid asc(SFunction<GridFSFile, ?> function) {
        return asc(true,function);
    }

    @Override
    public LambdaGrid asc(boolean condition, SFunction<GridFSFile, ?> function) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            this.query.with(Sort.by(Sort.Order.asc(field.getName())));
        }
        return this;
    }

    @Override
    public LambdaGrid elemMatch(SFunction<GridFSFile, ?> function, Criteria criteria) {
        Field field = findField(function);
        Assert.notNull(field,"无法获取引用字段");
        Criteria cri = Criteria.where(field.getName()).elemMatch(criteria);
        setCriteria(cri);
        return this;
    }

    @Override
    public LambdaGrid between(SFunction<GridFSFile, ?> function, Object var1, Object var2) {
        return between(true,function,var1,var2);
    }

    @Override
    public LambdaGrid between(boolean condition, SFunction<GridFSFile, ?> function, Object var1, Object var2) {
        if (condition){
            Field field = findField(function);
            Assert.notNull(field,"无法获取引用字段");
            String name = field.getName();
            if (Stream.of(var1, var2).anyMatch(Objects::nonNull)){
                if (Objects.nonNull(var1) && Objects.nonNull(var2)) {
                    Criteria cri1 = Criteria.where(name).gte(var1).lte(var2);
                    setCriteria(cri1);
                }else{
                    Object o = Optional.ofNullable(var1).orElse(var2);
                    if (o.equals(var1)){
                        setCriteria(Criteria.where(name).gte(var1));
                    }else{
                        setCriteria(Criteria.where(name).lte(var2));
                    }
                }
            }
        }
        return this;
    }

    @Override
    public PageImpl<GridFSFile> page(PageRequest pageRequest) {
        return page(query,pageRequest);
    }

    private void arrayConstructor(){
        if (!criterias.isEmpty()){
            Criteria[] criteria = new Criteria[criterias.size()];
            for (int i = 0; i < criterias.size(); i++) {
                criteria[i] = criterias.get(i);
            }
            query.addCriteria(new Criteria().andOperator(criteria));
        }
    }

    private void setCriteria(Criteria criteria){
        criterias.add(criteria);
    }


}
